package com.lovebugs;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;

import ognl.ClassResolver;
import ognl.DefaultClassResolver;
import ognl.DefaultMemberAccess;
import ognl.DefaultTypeConverter;
import ognl.Evaluation;
import ognl.MemberAccess;
import ognl.Node;
import ognl.OgnlOps;
import ognl.OgnlRuntime;
import ognl.TypeConverter;
/**
 * 实现OGNL功能
 * @author Administrator
 *
 */
public class OgnlContext extends Object implements Map {
	

	public static Configuration TEMPLATE_CFG = null;
	public static final String CONTEXT_CONTEXT_KEY = "context";
	public static final String ROOT_CONTEXT_KEY = "root";
	public static final String THIS_CONTEXT_KEY = "this";
	public static final String TRACE_EVALUATIONS_CONTEXT_KEY = "_traceEvaluations";
	public static final String LAST_EVALUATION_CONTEXT_KEY = "_lastEvaluation";
	public static final String KEEP_LAST_EVALUATION_CONTEXT_KEY = "_keepLastEvaluation";
	public static final String CLASS_RESOLVER_CONTEXT_KEY = "_classResolver";
	public static final String TYPE_CONVERTER_CONTEXT_KEY = "_typeConverter";
	public static final String MEMBER_ACCESS_CONTEXT_KEY = "_memberAccess";

	private static final String PROPERTY_KEY_PREFIX = "ognl";
	private static boolean DEFAULT_TRACE_EVALUATIONS = false;
	private static boolean DEFAULT_KEEP_LAST_EVALUATION = false;

	public static final ClassResolver DEFAULT_CLASS_RESOLVER = new DefaultClassResolver();
	public static final TypeConverter DEFAULT_TYPE_CONVERTER = new DefaultTypeConverter();
	public static final MemberAccess DEFAULT_MEMBER_ACCESS = new DefaultMemberAccess(false);

	private static Map RESERVED_KEYS = new HashMap(11);

	private Object root;
	private Object currentObject;
	private Node currentNode;
	private boolean traceEvaluations = DEFAULT_TRACE_EVALUATIONS;
	private Evaluation rootEvaluation;
	private Evaluation currentEvaluation;
	private Evaluation lastEvaluation;
	private boolean keepLastEvaluation = DEFAULT_KEEP_LAST_EVALUATION;
	private Map values = new HashMap(23);
	private ClassResolver classResolver = DEFAULT_CLASS_RESOLVER;
	private TypeConverter typeConverter = DEFAULT_TYPE_CONVERTER;
	private MemberAccess memberAccess = DEFAULT_MEMBER_ACCESS;

	static {
	  String s;

	  RESERVED_KEYS.put(CONTEXT_CONTEXT_KEY, null);
	  RESERVED_KEYS.put(ROOT_CONTEXT_KEY, null);
	  RESERVED_KEYS.put(THIS_CONTEXT_KEY, null);
	  RESERVED_KEYS.put(TRACE_EVALUATIONS_CONTEXT_KEY, null);
	  RESERVED_KEYS.put(LAST_EVALUATION_CONTEXT_KEY, null);
	  RESERVED_KEYS.put(KEEP_LAST_EVALUATION_CONTEXT_KEY, null);
	  RESERVED_KEYS.put(CLASS_RESOLVER_CONTEXT_KEY, null);
	  RESERVED_KEYS.put(TYPE_CONVERTER_CONTEXT_KEY, null);
	  RESERVED_KEYS.put(MEMBER_ACCESS_CONTEXT_KEY, null);

	  try {
	    if ((s = System.getProperty(PROPERTY_KEY_PREFIX + ".traceEvaluations")) != null) {
	      DEFAULT_TRACE_EVALUATIONS = Boolean.valueOf(s.trim()).booleanValue();
	    }
	    if ((s = System.getProperty(PROPERTY_KEY_PREFIX + ".keepLastEvaluation")) != null) {
	      DEFAULT_KEEP_LAST_EVALUATION = Boolean.valueOf(s.trim()).booleanValue();
	    }
	    /*加载模板目录*/
		String absulutepath = OgnlContext.class.getResource("/template").getPath();
		TEMPLATE_CFG = new Configuration();
		TEMPLATE_CFG.setDirectoryForTemplateLoading(new File(absulutepath));
		TEMPLATE_CFG.setObjectWrapper(new DefaultObjectWrapper());
	  } catch (Exception ex) {
	    // restricted access environment, just keep defaults
		  ex.printStackTrace();
	  }
	}

	/***
	 * Constructs a new OgnlContext with the default class resolver, type converter and
	 * member access.
	 */
	public OgnlContext() {
	  super();
	}

	/***
	 * Constructs a new OgnlContext with the given class resolver, type converter and
	 * member access.  If any of these parameters is null the default will be used.
	 */
	public OgnlContext(ClassResolver classResolver, TypeConverter typeConverter, MemberAccess memberAccess) {
	  this();
	  if (classResolver != null) {
	    this.classResolver = classResolver;
	  }
	  if (typeConverter != null) {
	    this.typeConverter = typeConverter;
	  }
	  if (memberAccess != null) {
	    this.memberAccess = memberAccess;
	  }
	}

	public OgnlContext(Map values) {
	  super();
	  this.values = values;
	}

	public OgnlContext(ClassResolver classResolver, TypeConverter typeConverter, MemberAccess memberAccess, Map values) {
	  this(classResolver, typeConverter, memberAccess);
	  this.values = values;
	}

	public void setValues(Map value) {
	  for (Iterator it = value.keySet().iterator(); it.hasNext();) {
	    Object k = it.next();
	    values.put(k, value.get(k));
	  }
	}

	public Map getValues() {
	  return values;
	}

	public void setClassResolver(ClassResolver value) {
	  if (value == null) {
	    throw new IllegalArgumentException("cannot set ClassResolver to null");
	  }
	  classResolver = value;
	}

	public ClassResolver getClassResolver() {
	  return classResolver;
	}

	public void setTypeConverter(TypeConverter value) {
	  if (value == null) {
	    throw new IllegalArgumentException("cannot set TypeConverter to null");
	  }
	  typeConverter = value;
	}

	public TypeConverter getTypeConverter() {
	  return typeConverter;
	}

	public void setMemberAccess(MemberAccess value) {
	  if (value == null) {
	    throw new IllegalArgumentException("cannot set MemberAccess to null");
	  }
	  memberAccess = value;
	}

	public MemberAccess getMemberAccess() {
	  return memberAccess;
	}

	public void setRoot(Object value) {
	  root = value;
	}

	public Object getRoot() {
	  return root;
	}

	public boolean getTraceEvaluations() {
	  return traceEvaluations;
	}

	public void setTraceEvaluations(boolean value) {
	  traceEvaluations = value;
	}

	public Evaluation getLastEvaluation() {
	  return lastEvaluation;
	}

	public void setLastEvaluation(Evaluation value) {
	  lastEvaluation = value;
	}

	/***
	 * This method can be called when the last evaluation has been used
	 * and can be returned for reuse in the free pool maintained by the
	 * runtime.  This is not a necessary step, but is useful for keeping
	 * memory usage down.  This will recycle the last evaluation and then
	 * set the last evaluation to null.
	 */
	public void recycleLastEvaluation() {
	  OgnlRuntime.getEvaluationPool().recycleAll(lastEvaluation);
	  lastEvaluation = null;
	}

	/***
	 * Returns true if the last evaluation that was done on this
	 * context is retained and available through <code>getLastEvaluation()</code>.
	 * The default is true.
	 */
	public boolean getKeepLastEvaluation() {
	  return keepLastEvaluation;
	}

	/***
	 * Sets whether the last evaluation that was done on this
	 * context is retained and available through <code>getLastEvaluation()</code>.
	 * The default is true.
	 */
	public void setKeepLastEvaluation(boolean value) {
	  keepLastEvaluation = value;
	}

	public void setCurrentObject(Object value) {
	  currentObject = value;
	}

	public Object getCurrentObject() {
	  return currentObject;
	}

	public void setCurrentNode(Node value) {
	  currentNode = value;
	}

	public Node getCurrentNode() {
	  return currentNode;
	}

	/***
	 * Gets the current Evaluation from the top of the stack.
	 * This is the Evaluation that is in process of evaluating.
	 */
	public Evaluation getCurrentEvaluation() {
	  return currentEvaluation;
	}

	public void setCurrentEvaluation(Evaluation value) {
	  currentEvaluation = value;
	}

	/***
	 * Gets the root of the evaluation stack.
	 * This Evaluation contains the node representing
	 * the root expression and the source is the root
	 * source object.
	 */
	public Evaluation getRootEvaluation() {
	  return rootEvaluation;
	}

	public void setRootEvaluation(Evaluation value) {
	  rootEvaluation = value;
	}

	/***
	 * Returns the Evaluation at the relative index given.  This should be
	 * zero or a negative number as a relative reference back up the evaluation
	 * stack.  Therefore getEvaluation(0) returns the current Evaluation.
	 */
	public Evaluation getEvaluation(int relativeIndex) {
	  Evaluation result = null;

	  if (relativeIndex <= 0) {
	    result = currentEvaluation;
	    while ((++relativeIndex < 0) && (result != null)) {
	      result = result.getParent();
	    }
	  }
	  return result;
	}

	/***
	 * Pushes a new Evaluation onto the stack.  This is done
	 * before a node evaluates.  When evaluation is complete
	 * it should be popped from the stack via <code>popEvaluation()</code>.
	 */
	public void pushEvaluation(Evaluation value) {
	  if (currentEvaluation != null) {
	    currentEvaluation.addChild(value);
	  } else {
	    setRootEvaluation(value);
	  }
	  setCurrentEvaluation(value);
	}

	/***
	 * Pops the current Evaluation off of the top of the stack.
	 * This is done after a node has completed its evaluation.
	 */
	public Evaluation popEvaluation() {
	  Evaluation result;

	  result = currentEvaluation;
	  setCurrentEvaluation(result.getParent());
	  if (currentEvaluation == null) {
	    setLastEvaluation(getKeepLastEvaluation() ? result : null);
	    setRootEvaluation(null);
	    setCurrentNode(null);
	  }
	  return result;
	}

	/**================= Map interface =================*/
	public int size() {
	  return values.size();
	}

	public boolean isEmpty() {
	  return values.isEmpty();
	}

	public boolean containsKey(Object key) {
	  return values.containsKey(key);
	}

	public boolean containsValue(Object value) {
	  return values.containsValue(value);
	}

	public Object get(Object key) {
	  Object result;

	  if (RESERVED_KEYS.containsKey(key)) {
	    if (key.equals(OgnlContext.THIS_CONTEXT_KEY)) {
	      result = getCurrentObject();
	    } else {
	      if (key.equals(OgnlContext.ROOT_CONTEXT_KEY)) {
	        result = getRoot();
	      } else {
	        if (key.equals(OgnlContext.CONTEXT_CONTEXT_KEY)) {
	          result = this;
	        } else {
	          if (key.equals(OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY)) {
	            result = getTraceEvaluations() ? Boolean.TRUE : Boolean.FALSE;
	          } else {
	            if (key.equals(OgnlContext.LAST_EVALUATION_CONTEXT_KEY)) {
	              result = getLastEvaluation();
	            } else {
	              if (key.equals(OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY)) {
	                result = getKeepLastEvaluation() ? Boolean.TRUE : Boolean.FALSE;
	              } else {
	                if (key.equals(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY)) {
	                  result = getClassResolver();
	                } else {
	                  if (key.equals(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY)) {
	                    result = getTypeConverter();
	                  } else {
	                    if (key.equals(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY)) {
	                      result = getMemberAccess();
	                    } else {
	                      throw new IllegalArgumentException("unknown reserved key '" + key + "'");
	                    }
	                  }
	                }
	              }
	            }
	          }
	        }
	      }
	    }
	  } else {
	    result = values.get(key);
	  }
	  return result;
	}

	public Object put(Object key, Object value) {
	  Object result;

	  if (RESERVED_KEYS.containsKey(key)) {
	    if (key.equals(OgnlContext.THIS_CONTEXT_KEY)) {
	      result = getCurrentObject();
	      setCurrentObject(value);
	    } else {
	      if (key.equals(OgnlContext.ROOT_CONTEXT_KEY)) {
	        result = getRoot();
	        setRoot(value);
	      } else {
	        if (key.equals(OgnlContext.CONTEXT_CONTEXT_KEY)) {
	          throw new IllegalArgumentException("can't change " + OgnlContext.CONTEXT_CONTEXT_KEY + " in context");
	        } else {
	          if (key.equals(OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY)) {
	            result = getTraceEvaluations() ? Boolean.TRUE : Boolean.FALSE;
	            setTraceEvaluations(OgnlOps.booleanValue(value));
	          } else {
	            if (key.equals(OgnlContext.LAST_EVALUATION_CONTEXT_KEY)) {
	              result = getLastEvaluation();
	              lastEvaluation = (Evaluation) value;
	            } else {
	              if (key.equals(OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY)) {
	                result = getKeepLastEvaluation() ? Boolean.TRUE : Boolean.FALSE;
	                setKeepLastEvaluation(OgnlOps.booleanValue(value));
	              } else {
	                if (key.equals(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY)) {
	                  result = getClassResolver();
	                  setClassResolver((ClassResolver) value);
	                } else {
	                  if (key.equals(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY)) {
	                    result = getTypeConverter();
	                    setTypeConverter((TypeConverter) value);
	                  } else {
	                    if (key.equals(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY)) {
	                      result = getMemberAccess();
	                      setMemberAccess((MemberAccess) value);
	                    } else {
	                      throw new IllegalArgumentException("unknown reserved key '" + key + "'");
	                    }
	                  }
	                }
	              }
	            }
	          }
	        }
	      }
	    }
	  } else {
	    result = values.put(key, value);
	  }
	  return result;
	}

	public Object remove(Object key) {
	  Object result;

	  if (RESERVED_KEYS.containsKey(key)) {
	    if (key.equals(OgnlContext.THIS_CONTEXT_KEY)) {
	      result = getCurrentObject();
	      setCurrentObject(null);
	    } else {
	      if (key.equals(OgnlContext.ROOT_CONTEXT_KEY)) {
	        result = getRoot();
	        setRoot(null);
	      } else {
	        if (key.equals(OgnlContext.CONTEXT_CONTEXT_KEY)) {
	          throw new IllegalArgumentException("can't remove " + OgnlContext.CONTEXT_CONTEXT_KEY + " from context");
	        } else {
	          if (key.equals(OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY)) {
	            throw new IllegalArgumentException("can't remove " + OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY + " from context");
	          } else {
	            if (key.equals(OgnlContext.LAST_EVALUATION_CONTEXT_KEY)) {
	              result = lastEvaluation;
	              setLastEvaluation(null);
	            } else {
	              if (key.equals(OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY)) {
	                throw new IllegalArgumentException("can't remove " + OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY + " from context");
	              } else {
	                if (key.equals(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY)) {
	                  result = getClassResolver();
	                  setClassResolver(null);
	                } else {
	                  if (key.equals(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY)) {
	                    result = getTypeConverter();
	                    setTypeConverter(null);
	                  } else {
	                    if (key.equals(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY)) {
	                      result = getMemberAccess();
	                      setMemberAccess(null);
	                    } else {
	                      throw new IllegalArgumentException("unknown reserved key '" + key + "'");
	                    }
	                  }
	                }
	              }
	            }
	          }
	        }
	      }
	    }
	  } else {
	    result = values.remove(key);
	  }
	  return result;
	}

	public void putAll(Map t) {
	  for (Iterator it = t.keySet().iterator(); it.hasNext();) {
	    Object k = it.next();

	    put(k, t.get(k));
	  }
	}

	public void clear() {
	  values.clear();
	  setRoot(null);
	  setCurrentObject(null);
	  setRootEvaluation(null);
	  setCurrentEvaluation(null);
	  setLastEvaluation(null);
	  setCurrentNode(null);
	  setClassResolver(DEFAULT_CLASS_RESOLVER);
	  setTypeConverter(DEFAULT_TYPE_CONVERTER);
	  setMemberAccess(DEFAULT_MEMBER_ACCESS);
	}

	public Set keySet() {
	  /** Should root, currentObject, classResolver, typeConverter & memberAccess be included here? */
	  return values.keySet();
	}

	public Collection values() {
	  /** Should root, currentObject, classResolver, typeConverter & memberAccess be included here? */
	  return values.values();
	}

	public Set entrySet() {
	  /** Should root, currentObject, classResolver, typeConverter & memberAccess be included here? */
	  return values.entrySet();
	}

	public boolean equals(Object o) {
	  return values.equals(o);
	}

	public int hashCode() {
	  return values.hashCode();
	}
}
